 /*******************************************************************************
  * Copyright (c) 2000, 2004 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Common Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/cpl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.core.internal.watson;

 import org.eclipse.core.internal.dtree.*;
 import org.eclipse.core.internal.utils.Assert;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;

 /**
  * An implementation of the visitor pattern, for visiting the elements
  * of an ElementTreeDelta.
  */
 public class DeltaIterator {
     /** pre-order traversal means visit the root first, then the children */
     public static final int PRE_ORDER = 0;

     /** post-order means visit the children, and then the root */
     public static final int POST_ORDER = 1;

     /** traversal order */
     private final int order;

     private ElementTreeDelta elementTreeDelta;
     private DeltaDataTree deltaTree;
     private ElementTree oldTree;
     private ElementTree newTree;

     /**
      * Creates a new DeltaIterator that traverses the tree in pre order.
      */
     public DeltaIterator() {
         order = PRE_ORDER;
     }

     /**
      * Creates a new DeltaIterator that traverses the tree in the specified manner.
      */
     public DeltaIterator(int order) {
         this.order = order;
     }

     /**
      * Initializes the iterator
      */
     private void initialize(ElementTreeDelta tree) {
         elementTreeDelta = tree;
         deltaTree = tree.getDeltaTree();
         oldTree = tree.getParent();
         newTree = tree.getElementTree();
     }

     /**
      * Iterates through the given element tree delta and visits each element,
      * passing in the element's path and element object.
      */
     public void iterate(ElementTreeDelta tree, IDeltaVisitor visitor) {
         iterate(tree, visitor, Path.ROOT);
     }

     /**
      * Iterates through the given element tree delta and visits each element
      * in the subtree rooted at the given path, passing in the element's path
      * and element object.
      */
     public void iterate(ElementTreeDelta tree, IDeltaVisitor visitor, IPath path) {
         initialize(tree);

         /* don't visit the root element */
         if (path.isRoot()) {
             IPath[] children = deltaTree.getChildren(path);
             for (int i = 0; i < children.length; i++) {
                 iterate(visitor, children[i], NodeComparison.K_CHANGED);
             }
         } else {
             iterate(visitor, path);
         }
     }

     /**
      * Starts the iteration at the provided entry point. If the entry
      * point is not in the real delta, the iteration terminates.
      */
     private void iterate(IDeltaVisitor visitor, IPath path) {
         /* find which tree this element is in */
         DataTreeNode node = (DataTreeNode) deltaTree.findNodeAt(path);

         if (node == null) {
             /* look in old tree */
             if (oldTree.includes(path)) {
                 iterate(visitor, path, NodeComparison.K_ADDED);
             } else {
                 /* look in new tree */
                 if (newTree.includes(path)) {
                     iterate(visitor, path, NodeComparison.K_REMOVED);
                 }
             }
         } else {
             /* look in the delta tree */
             iterate(visitor, path, NodeComparison.K_CHANGED);
         }
     }

     /**
      * Iterates from the given node, based on the kind of change in the parent.
      */
     private void iterate(IDeltaVisitor visitor, IPath path, int parentChange) {
         int comparison = 0, realChange = 0;
         Object oldData = null;
         Object newData = null;
         IPath[] children = null;

         switch (parentChange) {
             case NodeComparison.K_ADDED :
                 /* look in the new tree only */
                 comparison = realChange = parentChange;
                 newData = newTree.getElementData(path);
                 children = newTree.getChildren(path);
                 break;
             case NodeComparison.K_REMOVED :
                 /* look in the old tree only */
                 comparison = realChange = parentChange;
                 oldData = oldTree.getElementData(path);
                 children = oldTree.getChildren(path);
                 break;
             case NodeComparison.K_CHANGED :
                 /* look in delta tree */
                 NodeComparison info = (NodeComparison) deltaTree.getData(path);
                 comparison = info.getUserComparison();

                 realChange = info.getComparison();
                 children = deltaTree.getChildren(path);
                 oldData = info.getOldData();
                 newData = info.getNewData();
         }

         Assert.isNotNull(children);

         boolean visitChildren = true;
         if (order == PRE_ORDER) {
             visitChildren = visitor.visitElement(elementTreeDelta, path, oldData, newData, comparison);
         }

         if (visitChildren) {
             for (int i = 0; i < children.length; i++) {
                 iterate(visitor, children[i], realChange);
             }
         }

         if (order == POST_ORDER) {
             visitor.visitElement(elementTreeDelta, path, oldData, newData, comparison);
         }
     }
 }
